#!/usr/bin/python3
# -*- coding: utf-8 -*-

"""
# @version: v1.0
# @author : wlisingle
# @Email : 19688513@qq.com
# @Project : g-carbon-bio
# @File : historical_backtester.py
# @Software: PyCharm
# @time: 2025/3/11 17:01
# @description : 股票历史数据回测程序
"""

import math
from datetime import datetime
import pandas as pd  # 确保导入 pandas 库
from service.models.stock_historical_data_with_ma import StockHistoricalDataWithMA
from service.trade_date_calendar import TradeDateCalendar
from strategies.historical_stockdata_fetcher import HistoricalStockDataFetcher


class HistoricalBacktester:
    def __init__(self):
        self.trade_calendar = TradeDateCalendar()
        self.stock_historical = HistoricalStockDataFetcher()

    def get_trade_dates(self, code: str, current_date: datetime, offset_days: int = 250):
        """获取起始和结束日期以及相关历史数据"""
        if isinstance(current_date, str):
            current_date = datetime.strptime(current_date, "%Y-%m-%d").date()

        previous_start_date, next_day = self.trade_calendar.get_start_end_date(
            current_date.strftime("%Y-%m-%d"), offset_days
        )

        historical_data_list = self.stock_historical.fetch_stock_data(code, previous_start_date, next_day)
        moving_averages = self.calculate_moving_averages(historical_data_list)

        historical_data_with_ma = [
            StockHistoricalDataWithMA(
                date=data.date,
                stock_code=data.stock_code,
                open_price=data.open_price,
                close_price=data.close_price,
                high_price=data.high_price,
                low_price=data.low_price,
                volume=data.volume,
                turnover=data.turnover,
                amplitude=data.amplitude,
                change_rate=data.change_rate,
                change_amount=data.change_amount,
                turnover_rate=data.turnover_rate,
                ma5=ma_data['ma5'],
                ma10=ma_data['ma10'],
                ma20=ma_data['ma20'],
                ma30=ma_data['ma30'],
                ma60=ma_data['ma60'],
                ma120=ma_data['ma120']
            )
            for data, ma_data in zip(historical_data_list, moving_averages)
        ]

        return previous_start_date, next_day, historical_data_with_ma

    def calculate_moving_averages(self, historical_data_list):
        """计算移动平均线"""
        close_prices = [data.close_price for data in historical_data_list]
        dates = [data.date for data in historical_data_list]

        moving_averages = []
        for i in range(len(close_prices)):
            ma_data = {
                'date': dates[i],
                'ma5': self.calculate_ma(close_prices[:i + 1], 5),
                'ma10': self.calculate_ma(close_prices[:i + 1], 10),
                'ma20': self.calculate_ma(close_prices[:i + 1], 20),
                'ma30': self.calculate_ma(close_prices[:i + 1], 30),
                'ma60': self.calculate_ma(close_prices[:i + 1], 60),
                'ma120': self.calculate_ma(close_prices[:i + 1], 120)
            }
            moving_averages.append(ma_data)

        return moving_averages

    def calculate_ma(self, prices, period):
        """计算指定周期的移动平均，保留两位小数"""
        if len(prices) < period:
            return None
        ma = sum(prices[-period:]) / period
        return round(ma, 2)  # 保留两位小数

    @staticmethod
    def convert_to_dataframe(historical_data_with_ma):
        """将历史数据转换为 DataFrame"""
        data = {
            '日期': [],
            '股票代码': [],
            '开盘价': [],
            '收盘价': [],
            '最高价': [],
            '最低价': [],
            '成交量': [],
            '成交额': [],
            '振幅': [],
            '涨跌幅': [],
            '涨跌额': [],
            '换手率': [],
            '5日均线': [],
            '10日均线': [],
            '20日均线': [],
            '30日均线': [],
            '60日均线': [],
            '120日均线': []
        }

        for item in historical_data_with_ma:
            data['日期'].append(item.date)
            data['股票代码'].append(item.stock_code)
            data['开盘价'].append(item.open_price)
            data['收盘价'].append(item.close_price)
            data['最高价'].append(item.high_price)
            data['最低价'].append(item.low_price)
            data['成交量'].append(item.volume)
            data['成交额'].append(item.turnover)
            data['振幅'].append(item.amplitude)
            data['涨跌幅'].append(item.change_rate)
            data['涨跌额'].append(item.change_amount)
            data['换手率'].append(item.turnover_rate)
            data['5日均线'].append(item.ma5)
            data['10日均线'].append(item.ma10)
            data['20日均线'].append(item.ma20)
            data['30日均线'].append(item.ma30)
            data['60日均线'].append(item.ma60)
            data['120日均线'].append(item.ma120)

        return pd.DataFrame(data)

    def calculate_fee(self, total_cost):
        """根据交易总金额计算手续费（万分之五，向上取整）"""
        fee = total_cost * 0.0005  # 按照万分之五计算手续费
        return math.ceil(fee)  # 向上取整到最接近的整数

    def backtest_strategy(self, historical_data_with_ma, initial_capital=5000):
        """执行策略回测"""
        df = self.convert_to_dataframe(historical_data_with_ma)

        # 计算20日均线
        df['MA20'] = df['收盘价'].rolling(window=20).mean()

        # 创建信号列
        df['Signal'] = 0  # 默认不持仓
        df['交易类型'] = None
        df['交易金额'] = 0.0
        df['交易股数'] = 0
        df['交易价格'] = 0.0
        df['交易手续费'] = 0.0
        df['利润'] = 0.0

        capital = float(initial_capital)  # 确保初始资金为浮点数
        position = 0  # 当前持仓数量
        df['Cash'] = round(capital, 2)  # 初始化现金为浮点型，保留两位小数
        df['持仓价值'] = 0.0

        # 计算涨跌幅
        df['涨跌幅'] = df['收盘价'].pct_change()

        # 遍历历史数据
        for i in range(len(df)):
            if i > 0 and not pd.isnull(df['MA20'].iloc[i]):  # 从第二天开始进行信号和交易判断，并确保MA20不为空
                # 信号生成逻辑
                if df['收盘价'].iloc[i] > df['MA20'].iloc[i]:  # 在均线上方才能买入
                    df.loc[i, 'Signal'] = 1  # 买入信号
                elif df['收盘价'].iloc[i] < df['MA20'].iloc[i] and position > 0:  # 在均线下方才能卖出
                    df.loc[i, 'Signal'] = -1  # 卖出信号

                # 处理买入逻辑
                if df['Signal'].iloc[i] == 1 and position == 0:  # 只有在没有持仓时才能买入
                    buy_price = df['收盘价'].iloc[i]
                    shares_to_buy = int(capital // buy_price)  # 计算可购买的股数

                    # 调整为购买整数股，以100股为单位
                    shares_to_buy = (shares_to_buy // 100) * 100

                    if shares_to_buy > 0:  # 确保可以购买
                        total_cost = shares_to_buy * buy_price  # 计算交易总金额
                        buy_fee = self.calculate_fee(total_cost)  # 计算手续费（万分之五，向上取整）

                        if total_cost + buy_fee <= capital:  # 确保能扣除手续费后仍在资金范围内
                            capital -= (total_cost + buy_fee)  # 更新现金
                            position += shares_to_buy  # 更新持仓
                            df.loc[i, '交易类型'] = '买入'
                            df.loc[i, '交易金额'] = round(total_cost + buy_fee, 2)  # 记录买入的金额
                            df.loc[i, '交易股数'] = shares_to_buy
                            df.loc[i, '交易价格'] = buy_price
                            df.loc[i, '交易手续费'] = buy_fee
                            df.loc[i, '利润'] = 0.00  # 买入时利润为0

                # 处理卖出逻辑
                elif df['Signal'].iloc[i] == -1 and position > 0:  # 只有在有持仓时才能卖出
                    sell_price = df['收盘价'].iloc[i]
                    income = position * sell_price  # 卖出总收入

                    # 计算买入成本 = 买入股数 * 买入价格 + 手续费（需要注意，这里使用买入时的价格来计算买入成本）
                    buy_cost = position * buy_price + self.calculate_fee(position * buy_price)

                    capital += income  # 更新现金
                    profit = income - buy_cost  # 计算利润
                    df.loc[i, '交易类型'] = '卖出'
                    df.loc[i, '交易金额'] = round(income, 2)  # 记录卖出的金额
                    df.loc[i, '交易股数'] = position
                    df.loc[i, '交易价格'] = sell_price
                    df.loc[i, '交易手续费'] = 0.0  # 卖出不收取费用
                    df.loc[i, '利润'] = round(profit, 2)  # 记录利润

                    position = 0  # 卖出后清空持仓

                # 更新现金和持仓价值，保持两位小数
                df.loc[i, 'Cash'] = round(float(capital), 2)
                df.loc[i, '持仓价值'] = round(position * df['收盘价'].iloc[i], 2) if position > 0 else 0

                # 计算总资产
                df.loc[i, '总资产'] = round(df.loc[i, 'Cash'] + df.loc[i, '持仓价值'], 2)

        return df


if __name__ == "__main__":
    """主函数"""
    stock_code = "002382"  # 示例股票代码
    current_date = datetime.now().date()  # 获取当前日期

    backtester = HistoricalBacktester()
    previous_start_date, next_day, historical_data_with_ma = backtester.get_trade_dates(stock_code, current_date)
    # 打印每条历史数据及其所有参数
    # for item in historical_data_with_ma:
    #     print(f"日期: {item.date}, "
    #           f"股票代码: {item.stock_code}, "
    #           f"开盘价: {item.open_price}, "
    #           f"收盘价: {item.close_price}, "
    #           f"最高价: {item.high_price}, "
    #           f"最低价: {item.low_price}, "
    #           f"成交量: {item.volume}手, "
    #           f"成交额: {item.turnover}元, "
    #           f"振幅: {item.amplitude}%, "
    #           f"涨跌幅: {item.change_rate}%, "
    #           f"涨跌额: {item.change_amount}元, "
    #           f"换手率: {item.turnover_rate}%, "
    #           f"5日均线: {item.ma5}, "
    #           f"10日均线: {item.ma10}, "
    #           f"20日均线: {item.ma20}, "
    #           f"30日均线: {item.ma30}, "
    #           f"60日均线: {item.ma60}, "
    #           f"120日均线: {item.ma120}")

    print(f"起始日期: {previous_start_date}, 结束日期: {next_day}")

    # 执行策略回测
    results_df = backtester.backtest_strategy(historical_data_with_ma, initial_capital=5000)

    # 输出回测结果，过滤掉没有交易的记录
    print("\n策略回测结果:")
    for index, row in results_df.iterrows():
        if row['交易类型'] is not None:  # 只输出有交易的记录
            print(f"日期: {row['日期']}, "
                  f"收盘价: {row['收盘价']:.2f}, "
                  f"买卖信号: {'买' if row['Signal'] == 1 else '卖' if row['Signal'] == -1 else '无'}, "
                  f"当前现金: {row['Cash']:.2f}, "
                  f"持仓价值: {row['持仓价值']:.2f}, "
                  f"总资产: {row['总资产']:.2f}, "
                  f"交易类型: {row['交易类型']}, "
                  f"交易金额: {row['交易金额']:.2f}, "
                  f"交易股数: {row['交易股数']}, "
                  f"交易价格: {row['交易价格']:.2f}, "
                  f"交易手续费: {row['交易手续费']:.2f}, "
                  f"利润: {row['利润']:.2f}")
